﻿using Protocol.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using ErrorEventHandler = Protocol.Interface.ErrorEventHandler;

namespace Protocol.TCP
{
    public class AsyncServer : IAsyncServer
    {
        /// <summary>
        /// 当服务端在等待连接/接收或发送出现错误时触发事件
        /// </summary>
        public event ErrorEventHandler OnError;
        /// <summary>
        /// 当服务端接收到客户端连接时时触发事件
        /// </summary>
        public event AcceptEventHandler OnAccept;
        /// <summary>
        /// 当服务端接收到消息时触发事件
        /// </summary>
        public event ReceiveEventHandler OnReceive;
        public TcpListener Server { get; private set; }
        public Dictionary<IPEndPoint, TcpClient> Client { get; private set; }
        public Dictionary<IPEndPoint, List<string>> Messages { get; private set; }
        public Encoding _Encoding { get; set; }
        public bool Running { get => loop; }

        readonly IPAddress local_address = IPAddress.Parse("127.0.0.1");
        public int Port { get; private set; }

        private bool loop = false;

        private readonly int max_buffer_size = 1024 * 1024 * 4;

        /// <summary>
        /// 默认使用ASCII编码
        /// </summary>
        /// <param name="port">端口</param>
        public AsyncServer(int port)
        {
            this._Encoding = Encoding.ASCII;
            this.Port = port;
            Server = new TcpListener(local_address, port);
            Client = new Dictionary<IPEndPoint, TcpClient>();
            Messages = new Dictionary<IPEndPoint, List<string>>();
        }
        /// <summary>
        /// 默认使用ASCII编码
        /// </summary>
        /// <param name="port">端口</param>
        /// <param name="encoding">编码</param>
        public AsyncServer(int port, Encoding encoding)
        {
            this._Encoding = encoding;
            this.Port = port;
            Server = new TcpListener(local_address, port);
            Client = new Dictionary<IPEndPoint, TcpClient>();
            Messages = new Dictionary<IPEndPoint, List<string>>();
        }

        public Task Start()
        {
            loop = true;
            Server.Start();
            return Run();
        }
        public void Stop()
        {
            loop = false;
            Server.Stop();
            foreach (var kvp in Client)
            {
                try
                {
                    kvp.Value.Close();
                }
                catch (Exception e)
                {
                    OnError?.Invoke(e, ErrorType.ConnectError);
                }
            }
        }
        private async Task Run()
        {
            while (loop)
            {
                try
                {
                    var client = await Server.AcceptTcpClientAsync();
                    var ip = (IPEndPoint)client.Client.RemoteEndPoint;
                    Client[ip] = client;
                    StartReceive(ip, client);
                    OnAccept?.Invoke(client);
                }
                catch (Exception e)
                {
                    OnError?.Invoke(e, ErrorType.ConnectError);
                }

            }
        }

        public void Disconnect(IPEndPoint ip)
        {
            if (Client.ContainsKey(ip))
            {
                try
                {
                    Client[ip].Close();
                    Client.Remove(ip);
                }
                catch (Exception e)
                {
                    OnError?.Invoke(e, ErrorType.ConnectError);
                }
            }
        }

        public void Send(IPEndPoint ip, string msg)
        {
            if (Client.ContainsKey(ip))
            {
                try
                {
                    var client = Client[ip];
                    using (var sw = new StreamWriter(client.GetStream(), _Encoding))
                    {
                        sw.Write(msg);
                    }
                }
                catch (Exception e)
                {
                    OnError?.Invoke(e, ErrorType.SendError);
                }
            }
        }
        public async Task SendAsync(IPEndPoint ip, string msg)
        {
            if (Client.ContainsKey(ip))
            {
                try
                {
                    var client = Client[ip];
                    using (var sw = new StreamWriter(client.GetStream(), _Encoding))
                    {
                        await sw.WriteAsync(msg);
                    }
                }
                catch (Exception e)
                {
                    OnError?.Invoke(e, ErrorType.SendError);
                }
            }
        }

        private async void StartReceive(IPEndPoint ip, TcpClient client)
        {
            if (!Messages.ContainsKey(ip))
                Messages[ip] = new List<string>();
            var msg_list = Messages[ip];
            var buffer = new char[max_buffer_size];
            using (var sr = new StreamReader(client.GetStream(), _Encoding))
            {
                while (loop)
                {
                    try
                    {
                        var len = await sr.ReadAsync(buffer, 0, buffer.Length);
                        if (len > 0)
                        {
                            var msg = new string(buffer, 0, len);
                            lock (msg_list)
                            {
                                msg_list.Add(msg);
                            }
                            OnReceive?.Invoke(client, msg);
                        }
                    }
                    catch (Exception e)
                    {
                        OnError?.Invoke(e, ErrorType.ReceiveError);
                        Disconnect(ip);
                        break;
                    }
                }
            }

        }
    }
}
